/*
 * Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 * This file is part of e3log.
 *
 * e3log is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * e3log is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with e3log. If not, see <https://www.gnu.org/licenses/>.
 */

package e3log

import (
	"fmt"
	"github.com/fatih/color"
	jsoniter "github.com/json-iterator/go"
	"github.com/modern-go/reflect2"
	"hash/crc32"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

var _gObjStr sync.Map
var _gObjLock sync.Mutex

func objCheckType(obj LogMarshaller, msg string) (string, bool) {
	t := reflect2.TypeOf(obj).String()
	if t[0] == '*' {
		t = t[1:]
	}

	switch t {
	case "e3log.stack":
		t = gKeyNameStr[LogKeyCaller]
	case "e3log.stackList":
		t = gKeyNameStr[LogKeyStack]
	}

	if x, ok := _gObjStr.Load(t); ok {
		return t, x.(bool)
	}
	_gObjLock.Lock()
	var rs any

	err := jsoniter.ConfigFastest.UnmarshalFromString(msg, &rs)
	ok := err == nil
	_gObjStr.Store(t, ok)
	_gObjLock.Unlock()
	return t, ok
}

func logBytesToString(bs []byte) string {
	return unsafe.String(unsafe.SliceData(bs), len(bs))
}

func logStringToBytes(s string) []byte {
	p := unsafe.StringData(s)
	return unsafe.Slice(p, len(s))
}

func logColor(s string) *color.Color {
	hash := crc32.ChecksumIEEE(logStringToBytes(s))
	r := (hash & 0xFF0000) >> 16
	g := (hash & 0x00FF00) >> 8
	b := hash & 0x0000FF
	return color.RGB(int(r), int(g), int(b))
}

func setBackupName(fileName string, now time.Time, index int64) (string, int64) {
	filePath := filepath.Dir(fileName)
	baseName := filepath.Base(fileName)
	ext := filepath.Ext(fileName)
	prefix := strings.TrimSuffix(baseName, ext)
	timestamp := now.Format(TimeLayoutBackUp)
	var newFileName string
	for {
		newBaseName := prefix + timestamp + "-" + strconv.FormatInt(index, 10) + ext
		index++
		newFileName = filepath.Join(filePath, newBaseName)
		_, err := os.Stat(newFileName)
		if os.IsNotExist(err) {
			return newFileName, index
		}
	}
}

func parseBackupName(name, prefix, ext string) (time.Time, int64) {
	timePart := strings.TrimPrefix(name, prefix)
	timePart = strings.TrimSuffix(timePart, ext)
	parts := strings.Split(timePart, "-")
	if len(parts) != 2 {
		return time.Time{}, 0
	}

	timestamp, err := time.Parse(TimeLayoutBackUp, parts[0])
	if err != nil {
		return time.Time{}, 0
	}

	index, err := strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return time.Time{}, 0
	}

	return timestamp, index
}

func consoleEasyRead(level Level, cl *color.Color, bs []byte, noColor bool) string {
	var oldMap map[string]any
	err := jsoniter.ConfigFastest.Unmarshal(bs, &oldMap)
	if err != nil {
		return logBytesToString(bs) // 如果解析失败，返回原始字符串
	}

	newMap := make(map[string]any)

	for _, field := range gKeyNameStr {
		if val, ok := oldMap[field]; ok {
			newMap[field] = val
			delete(oldMap, field)
		}
	}

	msg := ""
	if val, ok := newMap[gKeyNameStr[LogKeyMsg]]; ok {
		msg = val.(string)
		delete(newMap, gKeyNameStr[LogKeyMsg])
	}

	// 构建输出字符串
	output := strings.Builder{}
	if ts, ok := newMap[gKeyNameStr[LogKeyTime]]; ok {
		output.WriteString(ts.(string))
		output.WriteString(" | ")
	}

	if prefix, ok := newMap[gKeyNameStr[LogKeyPrefix]]; ok {
		if noColor {
			output.WriteString(prefix.(string))
		} else {
			colorPrefix := logColor(prefix.(string))
			output.WriteString(colorPrefix.Sprint(prefix.(string)))
		}
		output.WriteString(" | ")
	}

	if caller, ok := newMap[gKeyNameStr[LogKeyCaller]]; ok {
		callerMap := caller.(map[string]any)
		output.WriteString(callerMap["File"].(string))
		output.WriteString(":")
		output.WriteString(fmt.Sprintf("%v", callerMap["Line"]))
		output.WriteString(" | ")
	}

	if lv, ok := newMap[gKeyNameStr[LogKeyLevel]]; ok {
		if noColor {
			output.WriteString(lv.(string))
		} else {
			output.WriteString(gLevelColors[level].Sprint(lv.(string)))
		}
		output.WriteString(" | ")
	}

	if noColor {
		output.WriteString(msg)
	} else {
		if cl != nil {
			output.WriteString(cl.Sprint(msg))
		} else {
			output.WriteString(gLevelColors[level].Sprint(msg))
		}
	}

	if len(oldMap) > 0 {
		remainingJson, err2 := jsoniter.ConfigFastest.Marshal(oldMap)
		if err2 == nil {
			output.WriteString(" | ")
			output.WriteString(logBytesToString(remainingJson))
		}
	}

	if callers, ok := newMap[gKeyNameStr[LogKeyStack]]; ok {
		output.WriteString(fmt.Sprintf("\nstack:%v", len(callers.([]any))))
		for _, caller := range callers.([]any) {
			callerMap := caller.(map[string]any)
			output.WriteString("\n -> ")
			output.WriteString(fmt.Sprintf("%-40s", callerMap["Func"].(string)))
			output.WriteString("\t")
			output.WriteString(callerMap["File"].(string))
			output.WriteString(":")
			output.WriteString(fmt.Sprintf("%v", callerMap["Line"]))
		}
	}
	output.WriteString("\n")
	return output.String()
}

func formatReflect(v any) string {
	if v == nil {
		return "nil"
	}
	val := reflect.ValueOf(v)
	switch val.Kind() {
	case reflect.Ptr:
		if val.IsNil() {
			return "nil"
		}
		return fmt.Sprintf("*%#v", val.Elem().Interface())
	case reflect.Slice:
		if val.IsNil() {
			return "nil"
		}
		result := "["
		for i := 0; i < val.Len(); i++ {
			if i > 0 {
				result += ", "
			}
			elem := val.Index(i)
			if elem.Kind() == reflect.Ptr {
				if elem.IsNil() {
					result += "nil"
				} else {
					result += fmt.Sprintf("*%#v", elem.Elem().Interface())
				}
			} else {
				result += fmt.Sprintf("%#v", elem.Interface())
			}
		}
		result += "]"
		return result

	case reflect.Map:
		if val.IsNil() {
			return "nil"
		}
		result := "map["
		keys := val.MapKeys()
		for i, key := range keys {
			if i > 0 {
				result += " "
			}
			value := val.MapIndex(key)
			if value.Kind() == reflect.Ptr {
				if value.IsNil() {
					result += fmt.Sprintf("%#v:nil", key.Interface())
				} else {
					result += fmt.Sprintf("%#v:*%#v", key.Interface(), value.Elem().Interface())
				}
			} else {
				result += fmt.Sprintf("%#v:%#v", key.Interface(), value.Interface())
			}
		}
		result += "]"
		return result
	default:
		return fmt.Sprintf("%#v", v)
	}
}
